home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / sched / schedule.c < prev    next >
C/C++ Source or Header  |  1991-08-09  |  39KB  |  1,480 lines

  1. /* 
  2.  * schedule.c --
  3.  *
  4.  *      Routines to implement the fair share scheduler algorithm.
  5.  *
  6.  * Copyright 1986 Regents of the University of California
  7.  * All rights reserved.
  8.  */
  9.  
  10. #ifndef lint
  11. static char rcsid[] = "$Header: /sprite/src/kernel/sched/RCS/schedule.c,v 9.12 91/08/09 13:28:28 mendel Exp $ SPRITE (Berkeley)";
  12. #endif /* not lint */
  13.  
  14. #include <sprite.h>
  15. #include <sched.h>
  16. #include <schedInt.h>
  17. #include <proc.h>
  18. #include <list.h>
  19. #include <timer.h>
  20. #include <sync.h>
  21. #include <sys.h>
  22. #include <dbg.h>
  23. #include <mach.h>
  24. #include <bstring.h>
  25. #include <stdio.h>
  26.  
  27. #ifdef spur
  28. #include <devCC.h>
  29. #endif
  30.  
  31. #ifdef sequent
  32. #include "machSGSProc.h"
  33. #include "devClockArbiter.h"    /* for blinky lights */
  34. #endif /* sequent */
  35.  
  36. static int    foundOnDeck[MACH_MAX_NUM_PROCESSORS];
  37. static int    foundInQueue[MACH_MAX_NUM_PROCESSORS];
  38. static int    missedStack[MACH_MAX_NUM_PROCESSORS];
  39.  
  40. /*
  41.  *  The basic philosophy is that processes that have not executed
  42.  *  as much as other processes deserve to be run first.  Thus we
  43.  *  keep a smoothed average of recent CPU usage (the more recent the
  44.  *  usage, the higher the weighting).  The process with the lowest
  45.  *  recent usage gets highest scheduling priority.  The smoothed
  46.  *  average is maintained by adding CPU usage as the process accumulates
  47.  *  it, then periodically (once a second) reducing all the usages of
  48.  *  all processes by a specific factor.  Thus, if a process stops using
  49.  *  the CPU then its average will gradually decay to zero;  if a process
  50.  *  becomes CPU-intensive, its average will gradually increase, up to
  51.  *  a maximum value.  The controlling parameters are:
  52.  *
  53.  *  FORGET_INTERVAL -    How often to reduce everyone's usage.
  54.  *  FORGET_MULTIPLY -
  55.  *  FORGET_SHIFT -    These two factors determine how CPU usage decays:
  56.  *            every second, everyone's CPU usage is multiplied
  57.  *            by FORGET_MULTIPLY, then shifted right by
  58.  *            FORGET_SHIFT.  Right now, the combined effect of
  59.  *            these two is to "forget" 1/8th of the process's
  60.  *            usage.
  61.  */
  62.  
  63. #define FORGET_MULTIPLY        14
  64. #define FORGET_SHIFT        4
  65. #define FORGET_INTERVAL        timer_IntOneSecond
  66.  
  67. /*  
  68.  *  The half-life of the average in seconds can be computed using this formula:
  69.  *
  70.  *        half-life  = ln(2) / ln(F)
  71.  *
  72.  *  where F = (FORGET_MULTIPLY)/(2**FORGET_SHIFT).  For the current settings
  73.  *  the half-life is about 5.1 seconds.  This means that if a process
  74.  *  suddenly stops executing, its usage will decay to half its early value
  75.  *  in about 5 seconds.  The half-life gives an idea of how responsive the
  76.  *  scheduler is to changes in process behavior.  If it responds too slowly,
  77.  *  then a previously-idle process could become CPU-bound and monopolize the
  78.  *  whole CPU for a long time until its usage rises.  If the half-life is
  79.  *  too short, then an interactive process that does anything substantial
  80.  *  (e.g. dragging a selection) will instantly lose its scheduling priority
  81.  *  relative to other compute-bound processes.
  82.  */
  83.  
  84. /*
  85.  * The scheduler module mutex semaphore.  Used in sync module as well,
  86.  * since synchronization involves mucking with the process queues.
  87.  */
  88. Sync_Semaphore sched_Mutex ; 
  89. Sync_Semaphore *sched_MutexPtr = &sched_Mutex;
  90.  
  91. /*
  92.  * Flag to see if Sched_Init has been called.  Used by Sched_GatherProcessInfo
  93.  * to know when things have been initialized. It's needed because GPI
  94.  * is called from the timer module and possibly before Sched_Init has been
  95.  * called.
  96.  */
  97. static Boolean init = FALSE;
  98.  
  99. /*
  100.  * Global variable for the timer queue for Sched_ForgetUsage.
  101.  */
  102. static Timer_QueueElement forgetUsageElement;
  103.  
  104. /*
  105.  * Structure for instrumentation.
  106.  */
  107. Sched_Instrument sched_Instrument;
  108.  
  109. #ifdef SOSP91
  110. /*
  111.  * Get overall user and system time too, rather than just per-process.
  112.  */
  113.  
  114. Sched_OverallTimes      sched_OverallTimesPerProcessor[MACH_MAX_NUM_PROCESSORS];
  115.  
  116. #include <sospRecord.h>
  117. Timer_Ticks nameTime[10] = {0};
  118. #endif /* SOSP91 */
  119.  
  120.  
  121.  
  122. /*
  123.  * Status of each processor.
  124.  */
  125. Sched_ProcessorStatus    sched_ProcessorStatus[MACH_MAX_NUM_PROCESSORS];
  126.  
  127. /*
  128.  * Length of time that a process can run before it is preempted.  This is
  129.  * expressed as a number of timer interrupts.  The quantum length and
  130.  * timer interrupt interval may not divide evenly.
  131.  */
  132.  
  133. int    sched_Quantum = SCHED_DESIRED_QUANTUM / TIMER_CALLBACK_INTERVAL_APPROX;
  134.  
  135.  
  136. Sched_OnDeck    sched_OnDeck[MACH_MAX_NUM_PROCESSORS];
  137.  
  138. /*
  139.  * Forward Declarations.
  140.  */
  141. static void RememberUsage _ARGS_((Proc_ControlBlock *curProcPtr));
  142. static Proc_ControlBlock *IdleLoop _ARGS_((void));
  143. static void QuantumEnd _ARGS_((Proc_ControlBlock *procPtr));
  144. extern void SchedPrintSchedStats _ARGS_((Timer_Ticks time, 
  145.                 ClientData clientData));
  146.  
  147.  
  148. /*
  149.  * ----------------------------------------------------------------------------
  150.  *
  151.  * Sched_Init --
  152.  *
  153.  *      Initialize data structures and variables for the scheduler.
  154.  *    Cause Sched_ForgetUsage to be called from timer callback queue.
  155.  *
  156.  * Results:
  157.  *      None.
  158.  *
  159.  * Side effects:
  160.  *      Global variables are initialized.  Run queue is initialized.
  161.  *
  162.  * ----------------------------------------------------------------------------
  163.  */
  164.  
  165. void
  166. Sched_Init()
  167. {
  168.     int    cpu;
  169.  
  170.     sched_ProcessorStatus[0] = SCHED_PROCESSOR_ACTIVE;
  171.     for(cpu = 0; cpu < MACH_MAX_NUM_PROCESSORS; cpu++) {
  172.     sched_ProcessorStatus[cpu] = SCHED_PROCESSOR_NOT_STARTED;
  173.     sched_OnDeck[cpu].procPtr = (Proc_ControlBlock *) NIL;
  174.     }
  175.     bzero((Address) &(sched_Instrument),sizeof(sched_Instrument));
  176.  
  177.     List_Init(schedReadyQueueHdrPtr);
  178.     Sync_SemInitDynamic(sched_MutexPtr, "sched_Mutex");
  179.     Sync_SemRegister(sched_MutexPtr);
  180.  
  181.     forgetUsageElement.routine        = Sched_ForgetUsage; 
  182.     forgetUsageElement.clientData    = 0;
  183.     forgetUsageElement.interval        = FORGET_INTERVAL;
  184.     Timer_ScheduleRoutine(&forgetUsageElement, TRUE);
  185.  
  186.     init = TRUE;
  187. }
  188.  
  189. /*
  190.  *----------------------------------------------------------------------
  191.  *
  192.  * Sched_ForgetUsage --
  193.  *
  194.  *    Adjusts the priority for all user processes on the system.
  195.  *  
  196.  *    This routine is called at regular intervals by the 
  197.  *    Timer module TimeOut routine.
  198.  *
  199.  *
  200.  * Results:
  201.  *    none.
  202.  *
  203.  * Side Effects:
  204.  *    Priorities of user processes are modified.
  205.  *
  206.  *----------------------------------------------------------------------
  207.  */
  208.  
  209. /*ARGSUSED*/
  210. void
  211. Sched_ForgetUsage(time, clientData)
  212.     Timer_Ticks time;    /* The absolute time when this routine is called. 
  213.              * (not used). */
  214.     ClientData    clientData;    /* 0 - not used. */
  215. {
  216.     register Proc_ControlBlock *procPtr;
  217.     register int i;
  218.  
  219.     /*
  220.      *  Gain exclusive access to usage fields in the process table.
  221.      */
  222.      MASTER_LOCK(sched_MutexPtr);
  223.  
  224.     /*
  225.      *  Loop through all the processes on the system and
  226.      *  forget some of the CPU usage for them.
  227.      */
  228.     for (i = 0; i < proc_MaxNumProcesses; i++) {
  229.     procPtr = proc_PCBTable[i];
  230.     if (procPtr->state == PROC_UNUSED) {
  231.         continue;
  232.     }
  233.         procPtr->unweightedUsage = 
  234.         (procPtr->unweightedUsage * FORGET_MULTIPLY) >> FORGET_SHIFT;
  235.  
  236.     procPtr->weightedUsage =
  237.         (procPtr->weightedUsage * FORGET_MULTIPLY) >> FORGET_SHIFT;
  238.     }
  239.  
  240.     /*
  241.      *  Schedule this procedure to be called again later.
  242.      */
  243.     Timer_ScheduleRoutine(&forgetUsageElement, TRUE);
  244.  
  245.     MASTER_UNLOCK(sched_MutexPtr);
  246. }
  247.  
  248.  
  249. /*
  250.  *----------------------------------------------------------------------
  251.  *
  252.  *  Sched_GatherProcessInfo --
  253.  *
  254.  *    This routine is called at every timer interrupt. It collects
  255.  *    statistics about the running process such as the state of CPU and
  256.  *    CPU usage. 
  257.  *
  258.  *  Results:
  259.  *    None.
  260.  *
  261.  *  Side Effects:
  262.  *    Various statistics about the running process are collected in the
  263.  *      process's control block.
  264.  *
  265.  *
  266.  *----------------------------------------------------------------------
  267.  */
  268. void
  269. Sched_GatherProcessInfo(interval)
  270.     unsigned int interval;    /* Number of ticks since last invocation. */
  271. {
  272.     register Proc_ControlBlock  *curProcPtr;
  273.     register int        cpu;
  274.  
  275.     if (!init) {
  276.     return;
  277.     }
  278.  
  279.     MASTER_LOCK(sched_MutexPtr);
  280.  
  281.     /*
  282.      *  Get a pointer to the current process from the array that keeps
  283.      *  track of running processes on each processor.
  284.      */
  285.     for (cpu = 0; cpu < mach_NumProcessors; cpu++) {
  286.  
  287.     curProcPtr = proc_RunningProcesses[cpu];
  288.  
  289.     /*
  290.      * If no process is currently running on this processor, don't
  291.      * charge the usage to a particular process but keep track of it.
  292.      */
  293.     if (curProcPtr == (Proc_ControlBlock *) NIL) {
  294.         Timer_AddIntervalToTicks(
  295.             sched_Instrument.processor[cpu].noProcessRunning, 
  296.             interval,
  297.             &(sched_Instrument.processor[cpu].noProcessRunning));
  298.         continue;
  299.     }
  300.  
  301.     /*
  302.      *  We want to gather statistics about how much CPU time is spent in
  303.      *  kernel and user states.  The processor state is determined by
  304.      *  calling a machine-dependent routine.
  305.      */
  306.     if (Mach_ProcessorState(cpu) == MACH_KERNEL) {
  307.         Timer_AddIntervalToTicks(curProcPtr->kernelCpuUsage.ticks, interval,
  308.                    &(curProcPtr->kernelCpuUsage.ticks));
  309. #ifdef SOSP91
  310.             Timer_AddIntervalToTicks(
  311.             sched_OverallTimesPerProcessor[cpu].kernelTime, interval,
  312.                     &(sched_OverallTimesPerProcessor[cpu].kernelTime));
  313.         {
  314.         int n;
  315.         n = curProcPtr->SOSP_IN_NAME_LOOKUP;
  316.         if (n>=0 && n<6) {
  317.             Timer_AddIntervalToTicks( nameTime[n], interval,
  318.                 &nameTime[n]);
  319.         } else {
  320.             /*
  321.              * We weren't initialized.
  322.              */
  323.             curProcPtr->SOSP_IN_NAME_LOOKUP = 0;
  324.         }
  325.         }
  326. #endif SOSP91
  327.     } else {
  328.         Timer_AddIntervalToTicks(curProcPtr->userCpuUsage.ticks, interval,
  329.                    &(curProcPtr->userCpuUsage.ticks));
  330. #ifdef SOSP91
  331.             Timer_AddIntervalToTicks(
  332.             sched_OverallTimesPerProcessor[cpu].userTime, interval,
  333.                     &(sched_OverallTimesPerProcessor[cpu].userTime));
  334.         if (curProcPtr->genFlags & PROC_FOREIGN) {
  335.         Timer_AddIntervalToTicks(
  336.             sched_OverallTimesPerProcessor[cpu].userTimeMigrated,
  337.             interval, &(
  338.             sched_OverallTimesPerProcessor[cpu].userTimeMigrated));
  339.         }
  340. #endif SOSP91
  341.     }
  342.  
  343.     /*
  344.      *  Update the CPU usage for scheduling priority calculations
  345.      *  for the current process.
  346.      */
  347.     curProcPtr->recentUsage += interval;
  348.  
  349.     /*
  350.      * See if the quantum has expired for the process.  It can go
  351.      * negative if the user process happened to be running in kernel mode
  352.      * when the quantum expired for the first time and the process has
  353.      * not reentered the kernel voluntarily.
  354.      */
  355.     if ((curProcPtr->genFlags & PROC_USER) && 
  356.         (curProcPtr->billingRate != PROC_NO_INTR_PRIORITY)) {
  357.         if (curProcPtr->schedQuantumTicks != 0) {
  358.         curProcPtr->schedQuantumTicks--;
  359.         }
  360.         if (curProcPtr->schedQuantumTicks == 0) {
  361.         QuantumEnd(curProcPtr);
  362.         }
  363.     }
  364.     }
  365.  
  366.     MASTER_UNLOCK(sched_MutexPtr);
  367. }
  368.  
  369.  
  370. /*
  371.  * ----------------------------------------------------------------------------
  372.  *
  373.  * Sched_ContextSwitchInt --
  374.  *
  375.  *    Change to a new process.  Set the state of the current process
  376.  *    to the state argument.
  377.  *
  378.  *    If no process is runnable, then loop with interrupts enabled and
  379.  *    the master lock released until one is found.
  380.  *
  381.  *    The master lock is assumed to be held with sched_Mutex when
  382.  *    this routine is called.
  383.  *
  384.  * Results:
  385.  *    None.
  386.  *
  387.  * Side effects:
  388.  *    A new process is made runnable.  Counters of context switches are
  389.  *    incremented.
  390.  *
  391.  * ----------------------------------------------------------------------------
  392.  */
  393.  
  394. void
  395. Sched_ContextSwitchInt(state)
  396.     register    Proc_State state;    /* New state of current process */
  397. {
  398.     register Proc_ControlBlock    *curProcPtr;      /* PCB for currently runnning 
  399.                          * process. */
  400.     register Proc_ControlBlock    *newProcPtr;      /* PCB for new process. */
  401.     Proc_ControlBlock        *tnewProcPtr;
  402.     register int cpu;
  403.  
  404.     cpu = Mach_GetProcessorNumber();
  405.     sched_Instrument.processor[cpu].numContextSwitches++;
  406.  
  407.     curProcPtr = Proc_GetCurrentProc();
  408.     /*
  409.      * If we have a context switch pending get rid of it.
  410.      */
  411.     curProcPtr->schedFlags &= ~SCHED_CONTEXT_SWITCH_PENDING;
  412.  
  413.     /*
  414.      * Adjust scheduling priorities.
  415.      */
  416.     RememberUsage(curProcPtr);
  417.     if (state == PROC_READY) {
  418.     /*
  419.      * If the current process is PROC_READY, add it to the ready queue and
  420.      * get the next runnable process.  If that happens to be the current
  421.      */
  422.     curProcPtr->numQuantumEnds++; 
  423.     if (List_IsEmpty(schedReadyQueueHdrPtr)) {
  424.         curProcPtr->schedQuantumTicks = sched_Quantum;
  425.         return;
  426.     }
  427.  
  428.     curProcPtr->state = PROC_READY;
  429.     Sched_InsertInQueue(curProcPtr, &tnewProcPtr);
  430.     newProcPtr = tnewProcPtr;
  431.     if (newProcPtr == (Proc_ControlBlock *) NIL) {
  432.         newProcPtr = IdleLoop();
  433.     } else if (newProcPtr == curProcPtr) {
  434.         curProcPtr->schedQuantumTicks = sched_Quantum;
  435.         curProcPtr->state = PROC_RUNNING;
  436.         return;
  437.     } 
  438.     /*
  439.       * Don't run this process if another processor is already using
  440.      * its stack.
  441.      */
  442.     if (newProcPtr->schedFlags & SCHED_STACK_IN_USE) {
  443.         Sched_InsertInQueue(newProcPtr, (Proc_ControlBlock **) NIL);
  444.         newProcPtr = IdleLoop();
  445.     } 
  446.     } else {
  447.     if (state == PROC_WAITING) {
  448.         curProcPtr->numWaitEvents++; 
  449.     }
  450.     curProcPtr->state = state;
  451.     /*
  452.      * Drop into the idle loop and come out with a runnable process.
  453.      * This procedure exists to try and capture idle time when profiling.
  454.      */
  455.     newProcPtr = IdleLoop();
  456.     }
  457.  
  458.     /*
  459.      * Set the state of the new process.  
  460.      */
  461.     newProcPtr->state = PROC_RUNNING;
  462.     newProcPtr->processor = cpu;
  463. #ifdef sun4
  464.     /*
  465.      * HACK.  The window overflow handler in the sparc mach module spills
  466.      * windows via the CurrentProc pointer when the user's stack is
  467.      * not resident. Before changing the CurrentProc pointer besure that
  468.      * no user windows are active in the register windows.  We need do
  469.      * this only if CurrentProc is changing.  The mach module should
  470.      * be fixed not to use CurrentProc anyway.
  471.      */
  472.      if (newProcPtr != curProcPtr) {
  473.      /*
  474.       * This is overkill because we only need flush the user's windows
  475.       * and not all (kernel and user) windows. It not real bad because
  476.       * we are about to do a Mach_ContextSwitch() which spills all
  477.       * windows anyway.
  478.       */
  479.     Mach_FlushWindowsToStack();
  480.     }
  481. #endif /* sun4 */
  482.     Proc_SetCurrentProc(newProcPtr);
  483.  
  484.     /*
  485.      * Set up the quantum as the number of clocks ticks that this process 
  486.      * is allowed to run berfore it is context-switched.
  487.      * (This field is ignored for kernel processes and user processes with 
  488.      * a billing rate of PROC_NO_INTR_PRIORITY, which allows them to run 
  489.      * forever.)
  490.      */
  491.     newProcPtr->schedQuantumTicks = sched_Quantum;
  492.  
  493.     /*
  494.      * If the current process is continuing, then don't bother to 
  495.      * to do full context switch.  
  496.      */
  497.     if (newProcPtr == curProcPtr) { 
  498.     return;
  499.     }
  500.  
  501.     sched_Instrument.processor[cpu].numFullCS++;
  502.  
  503.     /*
  504.      * Perform the hardware context switch.  After switching, make
  505.      * sure that there is a context for this process.
  506.      */
  507.     newProcPtr->schedFlags |= SCHED_STACK_IN_USE;
  508.     curProcPtr->schedFlags &= ~SCHED_STACK_IN_USE;
  509.     Mach_ContextSwitch(curProcPtr, newProcPtr);
  510. }
  511.  
  512.  
  513. /*
  514.  *----------------------------------------------------------------------
  515.  *
  516.  * RememberUsage --
  517.  *
  518.  *    Adjusts the weighted and unweighted CPU usages for a kernel or
  519.  *    and user process. A process with the billingRate of 
  520.  *    PROC_NO_INTR_PRIORITY does not get charged for weighted CPU usage,
  521.  *    which is used in deciding priority in the run queue.
  522.  *    
  523.  *    This routine assumes the sched_Mutex master lock is held.
  524.  *
  525.  * Results:
  526.  *    None.
  527.  *
  528.  * Side Effects:
  529.  *    CPU usages of the process are modified.
  530.  *
  531.  *----------------------------------------------------------------------
  532.  */
  533.  
  534. static void
  535. RememberUsage(curProcPtr)
  536.     register Proc_ControlBlock *curProcPtr;    /* The process that will be 
  537.                          * adjusted */
  538. {
  539.     register int billingRate = curProcPtr->billingRate;
  540.  
  541.     /*
  542.      *  We want to calculate the process's CPU usage at this moment.
  543.      *  There are 2 smoothed usage averages that we maintain: an
  544.      *  unweighted value and a weighted value.  The weighted usage is used
  545.      *  for calculating scheduling priority.  The unweighted usage keeps
  546.      *  track of the real smoothed usage.
  547.      */ 
  548.  
  549.     curProcPtr->unweightedUsage += curProcPtr->recentUsage;
  550.  
  551.     /*
  552.      *  The billing rate basically specifies a process's scheduling 
  553.      *  priority. It it used to modify the amount of the recent usage
  554.      *  that gets added to the weighted usage.
  555.      *
  556.      *  If the billing rate equals the normal value then the recent usage
  557.      *  is not multiplied or divided by any factor.  If the billing rate
  558.      *  is greater than the normal value then only a faction of the recent
  559.      *  usage is added to the weighted usage.  If the billing rate is less
  560.      *  than the normal value then the recent usage is multiplied by a
  561.      *  power of 2 before it is added to the weighted  usage.
  562.      *
  563.      *  A process with a billing rate of PROC_NO_INTR_PRIORITY does
  564.      *  not get charged for CPU usage.
  565.      */
  566.  
  567.  
  568.     if (billingRate >= PROC_NORMAL_PRIORITY) {
  569.     if (billingRate != PROC_NO_INTR_PRIORITY) {
  570.         curProcPtr->weightedUsage += curProcPtr->recentUsage >> billingRate;
  571.     }
  572.     } else {
  573.     curProcPtr->weightedUsage += curProcPtr->recentUsage << -(billingRate);
  574.     }
  575.  
  576.     /*
  577.      *  Reset the recent usage back to zero.
  578.      */
  579.  
  580.     curProcPtr->recentUsage = 0;
  581. }
  582.  
  583. /*
  584.  *----------------------------------------------------------------------
  585.  *
  586.  * IdleLoop --
  587.  *
  588.  *    This fetches a runnable process from the ready queue and returns it.
  589.  *    If none are available this goes into an idle loop, enabling and
  590.  *    disabling interrupts, and waits for something to become runnable.
  591.  *
  592.  * Results:
  593.  *    A pointer to the next process to run.
  594.  *
  595.  * Side effects:
  596.  *    Momentarily enables interrupts.
  597.  *
  598.  *----------------------------------------------------------------------
  599.  */
  600.  
  601. static Proc_ControlBlock *
  602. IdleLoop()
  603. {
  604.     register Proc_ControlBlock    *procPtr;
  605.     register int cpu;
  606.     register List_Links        *queuePtr;
  607.     register Boolean        foundOne;
  608.     Proc_ControlBlock        *lastProcPtr = Proc_GetCurrentProc();
  609.     Boolean            onReadyQueue;
  610. #ifdef spur 
  611.     /* Turn off perf counters. */
  612.     Dev_CCSetCounters(COUNTERS_OFF);
  613. #endif
  614.  
  615.     cpu = Mach_GetProcessorNumber();
  616.     queuePtr = schedReadyQueueHdrPtr;
  617.     if (sched_ProcessorStatus[cpu] == SCHED_PROCESSOR_ACTIVE) {
  618.     foundOne = FALSE;
  619.     procPtr = (Proc_ControlBlock *) List_First(queuePtr);
  620.     while (!List_IsAtEnd(queuePtr,(List_Links *) procPtr)) {
  621.         if (!(procPtr->schedFlags & SCHED_STACK_IN_USE) ||
  622.          (procPtr->processor == cpu)) {
  623.         foundOne = TRUE;
  624.         break; 
  625.         }
  626.         if (procPtr->schedFlags & SCHED_STACK_IN_USE) {
  627.         missedStack[cpu]++;
  628.         }
  629.         procPtr = (Proc_ControlBlock *)List_Next((List_Links *)procPtr);
  630.     }
  631.     if (foundOne) {
  632.         /*
  633.          * We found a READY process for us, break out of the
  634.          * idle loop.
  635.          */
  636.         onReadyQueue = TRUE;
  637.         foundInQueue[cpu]++;
  638. #ifdef spur
  639.         Mach_InstCountOff(0);
  640. #endif
  641.         goto exit;
  642.     }
  643.     }
  644. #ifdef sun4
  645.     /*
  646.      * HACK.  The window overflow handler in the sparc mach module spills
  647.      * windows via the CurrentProc pointer when the user's stack is
  648.      * not resident.  Before nuking the CurrentProc point besure
  649.      * that no user window is resident. THIS SHOULD BE FIXED.
  650.      */
  651.     Mach_FlushWindowsToStack();
  652. #endif /* sun4 */
  653.     Proc_SetCurrentProc((Proc_ControlBlock *) NIL);
  654. #ifdef spur
  655.     Mach_InstCountEnd(0);
  656. #endif
  657.     MASTER_UNLOCK(sched_MutexPtr);
  658.  
  659.     if (Mach_IntrNesting(cpu) != 0) {
  660.     int i;
  661.  
  662.     Mach_EnableIntr();
  663.     i = Mach_IntrNesting(cpu);
  664.     mach_NumDisableIntrsPtr[cpu] = 0;
  665.     Mach_EnableIntr();
  666.     panic("Interrupt level at %d going into idle loop.\n", i);
  667.     }
  668.  
  669. #ifdef sequent
  670.     /*
  671.      * Really going idle, turn off the front panel light
  672.      * and the processor board light.
  673.      */
  674.     if (light_show) {
  675.     if (fp_lights) {
  676.         FP_LIGHTOFF(cpu);
  677.     }
  678.     *(int *)PHYS_LED = 0;
  679.     }
  680. #endif /* sequent */
  681.  
  682.     while (1) {
  683.     /*
  684.      * Wait for a process to become runnable.  
  685.      */
  686.     if (((List_IsEmpty(queuePtr) == FALSE) ||
  687.          (sched_OnDeck[cpu].procPtr != (Proc_ControlBlock *) NIL)) &&
  688.         ((sched_ProcessorStatus[cpu] == SCHED_PROCESSOR_ACTIVE) ||
  689.          (sched_ProcessorStatus[cpu] == SCHED_PROCESSOR_COUNTING_TICKS) ||
  690.          (lastProcPtr->state == PROC_READY))) {
  691.         /*
  692.          * Looks like there might be something in the queue. We don't
  693.          * have sched_Mutex down at this point, so this is only a hint.
  694.          */
  695.         MASTER_LOCK(sched_MutexPtr);
  696. #ifdef spur
  697.         Mach_InstCountStart(2);
  698. #endif
  699.         /*
  700.          * Look and see if there is anything for us on deck.
  701.          */
  702.         procPtr = sched_OnDeck[cpu].procPtr;
  703.         if (procPtr != (Proc_ControlBlock *) NIL) {
  704.         if ((procPtr->schedFlags & SCHED_STACK_IN_USE) &&
  705.             (procPtr->processor != cpu)) {
  706.             panic("Process with stack in use in the staging area.");
  707.         }
  708.         sched_OnDeck[cpu].procPtr = (Proc_ControlBlock *) NIL;
  709.         onReadyQueue = FALSE;
  710.         foundOnDeck[cpu]++;
  711. #ifdef spur
  712.         Mach_InstCountOff(2);
  713. #endif
  714.         break;
  715.         }
  716.         /*
  717.          * If we are counting ticks then we are waiting for one 
  718.          * specific process to wake up, and it will show up in the
  719.          * staging area.  If we didn't find one there then skip to
  720.          * the bottom of the loop.
  721.          */
  722.         if (sched_ProcessorStatus[cpu] != SCHED_PROCESSOR_COUNTING_TICKS) {
  723.         /*
  724.          * Make sure queue is not empty. If there is a ready process
  725.          * take a peek at it to insure that we can execute it. The
  726.          * only condition preventing a processor from executing a
  727.          * process is that its stack is being used by another processor.
  728.          */
  729.         foundOne = FALSE;
  730.         procPtr = (Proc_ControlBlock *) List_First(queuePtr);
  731.         while (!List_IsAtEnd(queuePtr,(List_Links *) procPtr)) {
  732.             if (!(procPtr->schedFlags & SCHED_STACK_IN_USE) ||
  733.              (procPtr->processor == cpu)) {
  734.             foundOne = TRUE;
  735.             break; 
  736.             }
  737.             if (procPtr->schedFlags & SCHED_STACK_IN_USE) {
  738.             missedStack[cpu]++;
  739.             }
  740.             procPtr = (Proc_ControlBlock *)
  741.             List_Next((List_Links *)procPtr);
  742.         }
  743.         if (foundOne) {
  744.             /*
  745.              * We found a READY processor for us, break out of the
  746.              * idle loop.
  747.              */
  748.              onReadyQueue = TRUE;
  749.              foundInQueue[cpu]++;
  750. #ifdef spur
  751.             Mach_InstCountOff(2);
  752. #endif
  753.             break;
  754.         }
  755.         }
  756.         sync_InstrumentPtr[cpu]->sched_MutexMiss++;
  757. #ifdef spur
  758.         Mach_InstCountEnd(2);
  759. #endif
  760.         MASTER_UNLOCK(sched_MutexPtr);
  761.     }
  762.     /*
  763.      * Count Idle ticks.  
  764.      */
  765.     if (sched_Instrument.processor[cpu].idleTicksLow ==
  766.                     (unsigned) 0xffffffff) {
  767.         sched_Instrument.processor[cpu].idleTicksLow = 0;
  768.         sched_Instrument.processor[cpu].idleTicksOverflow++;
  769.     } else {
  770.         sched_Instrument.processor[cpu].idleTicksLow++;
  771.     }
  772.     }
  773. exit:
  774. #ifdef spur
  775.     Mach_InstCountStart(1);
  776. #endif
  777. #ifdef spur
  778.     Dev_CCSetCounters(COUNTERS_RESTORE); /* Restore perf counters. */
  779. #endif
  780.  
  781.     if (procPtr->state != PROC_READY) {
  782.     /*
  783.      * Unlock sched_Mutex because panic tries to grab it somewhere.
  784.      * Do the panic by hand, without syncing the disks, because
  785.      * we still deadlock someplace.
  786.      */
  787.     MASTER_UNLOCK(sched_MutexPtr);
  788.     printf("Fatal Error: Non-ready process found in ready queue.\n");
  789.     DBG_CALL;
  790.     MASTER_LOCK(sched_MutexPtr);
  791.     }
  792.     if (onReadyQueue == TRUE) {
  793.     ((List_Links *)procPtr)->prevPtr->nextPtr =
  794.                         ((List_Links *)procPtr)->nextPtr;
  795.     ((List_Links *)procPtr)->nextPtr->prevPtr =
  796.                         ((List_Links *)procPtr)->prevPtr;
  797.     /*
  798.     List_Remove((List_Links *)procPtr);
  799.     */
  800.     sched_Instrument.numReadyProcesses -= 1;
  801.     }
  802.  
  803. #ifdef sequent
  804.     /*
  805.      * Leaving idle, turn on the front panel light
  806.      * and the processor board light.
  807.      */
  808.     if (light_show) {
  809.     if (fp_lights) {
  810.         FP_LIGHTON(cpu);
  811.     }
  812.     *(int *)PHYS_LED = 1;
  813.     }
  814. #endif /* sequent */
  815.  
  816.     return(procPtr);
  817. }
  818.  
  819. /*
  820.  *----------------------------------------------------------------------
  821.  *
  822.  * Sched_TimeTicks --
  823.  *
  824.  *    Idle for a few seconds and count the ticks recorded in IdleLoop.
  825.  *    For now, we only do this for one processor. All we're trying to get
  826.  *    is a rough estimate of idleTicksPerSecond.
  827.  *
  828.  *      This procedure is called during boot. The results are pretty much
  829.  *    meaningless if it is not.
  830.  *    
  831.  *    For best results all interrupts except for timer interrupts should
  832.  *    be off.  If we are on a multiprocessor then we idle all processors
  833.  *    for first so they don't interfere as badly.  Interrupts will still
  834.  *    screw us up (they are handled by processor 1 but they still use
  835.  *    locks we may need),  but I don't think turning off interrupts
  836.  *    for 5 seconds on a live system is a good idea.
  837.  *
  838.  * Results:
  839.  *    None.
  840.  *
  841.  * Side effects:
  842.  *    Momentarily enables interrupts.
  843.  *
  844.  *----------------------------------------------------------------------
  845.  */
  846.  
  847. void
  848. Sched_TimeTicks()
  849. {
  850.     register int lowTicks;
  851.     register int cpu;
  852.     Time time;
  853.     int i;
  854.     Boolean    wasIdled[MACH_MAX_NUM_PROCESSORS];
  855.  
  856.     cpu = Mach_GetProcessorNumber(); 
  857.     if (cpu != 0) {
  858.     sched_ProcessorStatus[cpu] = SCHED_PROCESSOR_COUNTING_TICKS;
  859.     for (i = 0; i < mach_NumProcessors; i++) {
  860.          if (sched_ProcessorStatus[i] == SCHED_PROCESSOR_ACTIVE) {
  861.          (void) Sched_IdleProcessor(i);
  862.          wasIdled[i] = TRUE;
  863.          } else {
  864.          wasIdled[i] = FALSE;
  865.          }
  866.      }
  867.     }
  868.     Time_Multiply(time_OneSecond, 5, &time);
  869.     printf("Idling processor %d for 5 seconds...",cpu);
  870.     lowTicks = sched_Instrument.processor[cpu].idleTicksLow;
  871.     (void) Sync_WaitTime(time);
  872.     lowTicks = sched_Instrument.processor[cpu].idleTicksLow - lowTicks;
  873.     printf(" %d ticks\n", lowTicks);
  874.     sched_Instrument.processor[cpu].idleTicksPerSecond = lowTicks / 5;
  875.     sched_ProcessorStatus[cpu] = SCHED_PROCESSOR_ACTIVE;
  876.     if (cpu != 0) {
  877.     for (i = 0; i < mach_NumProcessors; i++) {
  878.          if (wasIdled[i]) {
  879.          (void) Sched_StartProcessor(i);
  880.          }
  881.      }
  882.      }
  883. }
  884.  
  885.  
  886. /*
  887.  *----------------------------------------------------------------------
  888.  *
  889.  * QuantumEnd --
  890.  *
  891.  *    Called by Sched_GatherProcessInfo when a process's quantum has expired.
  892.  *    A global flag is set to indicate that the current process should
  893.  *    be involuntarily context switched at the next available moment.
  894.  *    If the process is executing in kernel mode, then don't force a context
  895.  *    switch now, but instead mark the process as having a context switch
  896.  *    pending.
  897.  *
  898.  *    N.B. This routine assumes the sched mutex is already locked.
  899.  *
  900.  * Results:
  901.  *    None.
  902.  *
  903.  * Side effects:
  904.  *    A context switch is initiated.
  905.  *
  906.  *----------------------------------------------------------------------
  907.  */
  908.  
  909. static void
  910. QuantumEnd(procPtr)
  911.     register    Proc_ControlBlock     *procPtr;
  912. {
  913.     procPtr->schedFlags |= SCHED_CONTEXT_SWITCH_PENDING;
  914.     procPtr->specialHandling = 1;
  915.     if (procPtr->processor != Mach_GetProcessorNumber()) {
  916.     /* 
  917.      * If the process whose quantum has ended is running on a different
  918.      * processor we need to poke the processor and force it into the
  919.      * kernel. On its way back to user mode the special handling flag
  920.      * will be checked and a context switch will occur (assuming that
  921.      * the offending process is still running).
  922.      */
  923.     Mach_CheckSpecialHandling(procPtr->processor);
  924.     }
  925. }
  926.  
  927. /*
  928.  *----------------------------------------------------------------------
  929.  *
  930.  * Sched_PrintStat --
  931.  *
  932.  *    Print the sched module statistics.
  933.  *
  934.  * Results:
  935.  *    None.
  936.  *
  937.  * Side effects:
  938.  *    Do the prints.
  939.  *
  940.  *----------------------------------------------------------------------
  941.  */
  942. void
  943. Sched_PrintStat()
  944. {
  945.     Time  tmp;
  946.     int   i;
  947.  
  948.     printf("Sched Statistics\n");
  949.     for(i = 0; i < mach_NumProcessors;i++) {
  950.     printf("Processor: %d\n",i);
  951.     printf("numContextSwitches = %d\n",
  952.            sched_Instrument.processor[i].numContextSwitches);
  953.     printf("numFullSwitches    = %d\n",
  954.            sched_Instrument.processor[i].numFullCS);
  955.     printf("numInvoluntary     = %d\n",
  956.            sched_Instrument.processor[i].numInvoluntarySwitches);
  957.     Timer_TicksToTime(sched_Instrument.processor[i].noProcessRunning, &tmp);
  958.     printf("Idle Time          = %d.%06d seconds\n", 
  959.              tmp.seconds, tmp.microseconds);
  960.     }
  961. }
  962.  
  963.  
  964. /*
  965.  *----------------------------------------------------------------------
  966.  *
  967.  * Sched_LockAndSwitch --
  968.  *
  969.  *    Acquires the Master Lock and performs a context switch.
  970.  *    Called when a process's quantum has expired and a trace trap
  971.  *    exception has arisen with the sched_ContextSwitchInProgress flag set.
  972.  *
  973.  * Results:
  974.  *    None.
  975.  *
  976.  * Side effects:
  977.  *    A context switch is performed.  The count of involuntary switches is
  978.  *    incremented.
  979.  *
  980.  *----------------------------------------------------------------------
  981.  */
  982.  
  983. void
  984. Sched_LockAndSwitch()
  985. {
  986.     MASTER_LOCK(sched_MutexPtr);
  987.     sched_Instrument.processor[Mach_GetProcessorNumber()].
  988.                 numInvoluntarySwitches++;
  989.     Sched_ContextSwitchInt(PROC_READY);
  990. #ifdef spur
  991.     Mach_InstCountEnd(1);
  992. #endif
  993.  
  994.     MASTER_UNLOCK(sched_MutexPtr);
  995. }
  996.  
  997.  
  998. /*
  999.  *----------------------------------------------------------------------
  1000.  *
  1001.  * Sched_ContextSwitch --
  1002.  *
  1003.  *    Acquires the Master Lock and performs a context switch.
  1004.  *
  1005.  * Results:
  1006.  *    None.
  1007.  *
  1008.  * Side effects:
  1009.  *    A context switch is performed.
  1010.  *
  1011.  *----------------------------------------------------------------------
  1012.  */
  1013.  
  1014. void
  1015. Sched_ContextSwitch(state)
  1016.     Proc_State    state;
  1017. {
  1018.  
  1019.     MASTER_LOCK(sched_MutexPtr);
  1020.     Sched_ContextSwitchInt(state);
  1021. #ifdef spur
  1022.     Mach_InstCountEnd(1);
  1023. #endif
  1024.     MASTER_UNLOCK(sched_MutexPtr);
  1025.  
  1026. }
  1027.  
  1028.  
  1029.  
  1030. /*
  1031.  *----------------------------------------------------------------------
  1032.  *
  1033.  * Sched_StartKernProc --
  1034.  *
  1035.  *    Start a process by unlocking the master lock and calling the
  1036.  *    function whose address has been passed to us as an argument.
  1037.  *    If the function returns then exit.
  1038.  *
  1039.  *
  1040.  * Results:
  1041.  *    None.
  1042.  *
  1043.  * Side effects:
  1044.  *    The master lock is released.
  1045.  *
  1046.  *----------------------------------------------------------------------
  1047.  */
  1048. void
  1049. Sched_StartKernProc(func)
  1050.     void    (*func)();
  1051. {
  1052. #ifdef spur
  1053.     Mach_InstCountEnd(1);
  1054. #endif
  1055.     MASTER_UNLOCK(sched_MutexPtr);
  1056.     func();
  1057.     Proc_Exit(0);
  1058. }
  1059.  
  1060.  
  1061. /*
  1062.  *----------------------------------------------------------------------
  1063.  *
  1064.  * Sched_MakeReady --
  1065.  *
  1066.  *    Put the process on the ready queue.
  1067.  *
  1068.  * Results:
  1069.  *    None.
  1070.  *
  1071.  * Side effects:
  1072.  *    State of given process changed to ready.
  1073.  *
  1074.  *----------------------------------------------------------------------
  1075.  */
  1076.  
  1077. void
  1078. Sched_MakeReady(procPtr)
  1079.     register    Proc_ControlBlock    *procPtr;
  1080. {
  1081.     MASTER_LOCK(sched_MutexPtr);
  1082.     procPtr->state = PROC_READY;
  1083.     Sched_InsertInQueue(procPtr, (Proc_ControlBlock **) NIL);
  1084.     MASTER_UNLOCK(sched_MutexPtr);
  1085. }
  1086.  
  1087.  
  1088. /*
  1089.  *----------------------------------------------------------------------
  1090.  *
  1091.  * Sched_StartUserProc --
  1092.  *
  1093.  *    Start a user process running.  This is the first thing that is
  1094.  *     called when a newly created process begins execution.
  1095.  *
  1096.  * Results:
  1097.  *    None.
  1098.  *
  1099.  * Side effects:
  1100.  *    None.
  1101.  *
  1102.  *----------------------------------------------------------------------
  1103.  */
  1104.  
  1105. void
  1106. Sched_StartUserProc(pc)
  1107.     Address    pc;    /* Program counter where process is to start
  1108.              * executing. */
  1109. {
  1110.     register         Proc_ControlBlock *procPtr;
  1111.  
  1112. #ifdef spur
  1113.     Mach_InstCountEnd(1);
  1114. #endif
  1115.     MASTER_UNLOCK(sched_MutexPtr);
  1116.     procPtr = Proc_GetCurrentProc();
  1117.  
  1118. #ifdef notdef
  1119.     Proc_Lock(procPtr);
  1120.     procPtr->genFlags |= PROC_DONT_MIGRATE;
  1121.     Proc_Unlock(procPtr);
  1122. #endif
  1123.     
  1124.     /*
  1125.      * Start the process running.  This does not return.
  1126.      */
  1127.     Mach_StartUserProc(procPtr, pc);
  1128. }
  1129. #if (MACH_MAX_NUM_PROCESSORS != 1)
  1130.  
  1131.  
  1132. /*
  1133.  *----------------------------------------------------------------------
  1134.  *
  1135.  * ProcessorStartProcess --
  1136.  *
  1137.  *    The initial process of a processor.
  1138.  *
  1139.  * Results:
  1140.  *    None.
  1141.  *
  1142.  * Side effects:
  1143.  *    
  1144.  *
  1145.  *----------------------------------------------------------------------
  1146.  */
  1147.  
  1148. static 
  1149. void ProcessorStartProcess()
  1150. {
  1151.        /*
  1152.          * Detach from parent so that cleanup will occur when the
  1153.          * processor exits with this process. Also set the SCHED_STACK_IN_USE
  1154.          * flag so that cleanup wont happen too early.
  1155.          */
  1156.         Proc_Detach(SUCCESS);
  1157.         Sched_ContextSwitch(PROC_WAITING);
  1158.     Proc_Exit(0);
  1159. }
  1160.  
  1161.  
  1162.  
  1163. /*
  1164.  *----------------------------------------------------------------------
  1165.  *
  1166.  * StartProcessor --
  1167.  *
  1168.  *    Start up a processor..
  1169.  *
  1170.  * Results:
  1171.  *    None.
  1172.  *
  1173.  * Side effects:
  1174.  *    
  1175.  *
  1176.  *----------------------------------------------------------------------
  1177.  */
  1178.  
  1179. ReturnStatus
  1180. StartProcessor(pnum)
  1181.     int        pnum;        /* Processor number to start. */
  1182. {
  1183.     Proc_PID    pid;
  1184.     Proc_ControlBlock *procPtr;
  1185.     char        procName[128];
  1186.     ReturnStatus status;
  1187.  
  1188.     /*
  1189.      * Startup an initial process for the processor pnum.  
  1190.      */
  1191.     sprintf(procName,"Processor%dStart",pnum);
  1192.     Proc_NewProc((Address)ProcessorStartProcess, PROC_KERNEL, FALSE, &pid,
  1193.                     procName);
  1194.     procPtr = Proc_GetPCB(pid);
  1195.     /*
  1196.      * Wait for this processor to go into the WAIT state.
  1197.      */
  1198.     while (procPtr->state != PROC_WAITING) {
  1199.     (void) Sync_WaitTimeInterval(10 * timer_IntOneMillisecond);
  1200.     }
  1201.  
  1202.     /*
  1203.      * Wait for its stack to become free .
  1204.      */
  1205.     while (procPtr->schedFlags & SCHED_STACK_IN_USE) {
  1206.     (void) Sync_WaitTimeInterval(10 * timer_IntOneMillisecond);
  1207.     }
  1208.     Sched_ContextSwitch(PROC_READY);
  1209.     printf("Starting processor %d with pid 0x%x\n",pnum,pid);
  1210.     status = Mach_SpinUpProcessor(pnum,procPtr);
  1211.     if (status != SUCCESS) {
  1212.     printf("Warning: Processor %d not started.\n",pnum);
  1213.     }
  1214.     return (status);
  1215. }
  1216. #endif
  1217.  
  1218. /*
  1219.  *----------------------------------------------------------------------
  1220.  *
  1221.  * Sched_StartProcessor --
  1222.  *
  1223.  *    Start a processor running processes.
  1224.  *
  1225.  * Results:
  1226.  *    A return status.
  1227.  *
  1228.  * Side effects:
  1229.  *    A processor maybe started.
  1230.  *
  1231.  *----------------------------------------------------------------------
  1232.  */
  1233. ReturnStatus
  1234. Sched_StartProcessor(pnum)
  1235.     int        pnum;    /* Processor number to start. */
  1236. {
  1237.     ReturnStatus    status;
  1238.     /*
  1239.      * Insure that processor number is in range.   
  1240.      * 
  1241.      */
  1242.     if (pnum >= MACH_MAX_NUM_PROCESSORS) {
  1243.     return (GEN_INVALID_ARG);
  1244.     }
  1245.     MASTER_LOCK(sched_MutexPtr);
  1246.     switch (sched_ProcessorStatus[pnum]) { 
  1247.     case SCHED_PROCESSOR_IDLE: {
  1248.         sched_ProcessorStatus[pnum] = SCHED_PROCESSOR_ACTIVE;
  1249.         /*
  1250.          * Fall thru .
  1251.          */
  1252.     }
  1253.     case SCHED_PROCESSOR_STARTING:
  1254.     case SCHED_PROCESSOR_ACTIVE: {
  1255.         status = SUCCESS;
  1256.         break;
  1257.     }
  1258.     case SCHED_PROCESSOR_NOT_STARTED: {
  1259. #if (MACH_MAX_NUM_PROCESSORS != 1)
  1260.         sched_ProcessorStatus[pnum] == SCHED_PROCESSOR_STARTING;
  1261.         MASTER_UNLOCK(sched_MutexPtr);
  1262.         status = StartProcessor(pnum);
  1263.         return (status);
  1264. #endif
  1265.     } 
  1266.     default: {
  1267.         printf("Warning: Unknown processor state %d for processor %d\n",
  1268.             (int) sched_ProcessorStatus[pnum], pnum);
  1269.         status = FAILURE;
  1270.     }
  1271.     }
  1272.     MASTER_UNLOCK(sched_MutexPtr);
  1273.     return (status);
  1274. }
  1275.  
  1276.  
  1277.  
  1278. /*
  1279.  *----------------------------------------------------------------------
  1280.  *
  1281.  * Sched_IdleProcessor --
  1282.  *
  1283.  *    Put a processor into the idle state so it wont be scheduled for
  1284.  *    anymore processes.
  1285.  *
  1286.  * Results:
  1287.  *    None.
  1288.  *
  1289.  * Side effects:
  1290.  *    A processor will be idled started.
  1291.  *
  1292.  *----------------------------------------------------------------------
  1293.  */
  1294. ReturnStatus
  1295. Sched_IdleProcessor(pnum)
  1296.     int        pnum;    /* Processor number to start. */
  1297. {
  1298.     ReturnStatus    status;
  1299.     /*
  1300.      * Insure that processor number is in range.   
  1301.      * 
  1302.      */
  1303.  
  1304. #ifdef sequent
  1305.     if ((pnum < 0) || (pnum >= mach_NumProcessors)) {
  1306.         return GEN_INVALID_ARG;
  1307.     }
  1308. #else /* sequent */
  1309.     if (pnum >= MACH_MAX_NUM_PROCESSORS) {
  1310.     return (GEN_INVALID_ARG);
  1311.     }
  1312. #endif /* sequent */
  1313.     MASTER_LOCK(sched_MutexPtr);
  1314.     switch (sched_ProcessorStatus[pnum]) { 
  1315.     case SCHED_PROCESSOR_ACTIVE: 
  1316.         sched_ProcessorStatus[pnum] = SCHED_PROCESSOR_IDLE;
  1317.         /*
  1318.          * Fall thru.
  1319.          */
  1320.     case SCHED_PROCESSOR_IDLE: {
  1321.         status = SUCCESS;
  1322.         break;
  1323.     }
  1324.     case SCHED_PROCESSOR_NOT_STARTED: 
  1325.     case SCHED_PROCESSOR_STARTING: {
  1326.         status = GEN_INVALID_ARG;
  1327.         break;
  1328.     }
  1329.     default: {
  1330.         printf("Warning: Unknown processor state %d for processor %d\n",
  1331.             (int) sched_ProcessorStatus[pnum], pnum);
  1332.         status = FAILURE;
  1333.     }
  1334.     }
  1335.     MASTER_UNLOCK(sched_MutexPtr);
  1336.     return (status);
  1337. }
  1338.  
  1339.  
  1340. /*
  1341.  *----------------------------------------------------------------------
  1342.  *
  1343.  * Sched_DumpReadyQueue --
  1344.  *
  1345.  *    Print out the contents of the ready queue.
  1346.  *
  1347.  * Results:
  1348.  *    None.
  1349.  *
  1350.  * Side effects:
  1351.  *    Output goes to the screen.
  1352.  *
  1353.  *----------------------------------------------------------------------
  1354.  */
  1355.  
  1356. /* ARGSUSED */
  1357. void
  1358. Sched_DumpReadyQueue(dummy)
  1359.     ClientData dummy;
  1360. {
  1361.     List_Links *itemPtr;
  1362.     Proc_ControlBlock *snapshot[SCHED_MAX_DUMP_SIZE];
  1363.     int snapshotCnt;
  1364.     int overflow;
  1365.     int i;
  1366.  
  1367.     if (List_IsEmpty(schedReadyQueueHdrPtr)) {
  1368.     printf("\nReady queue is empty.\n");
  1369.     } else {
  1370.     printf("\n%8s %5s %10s %10s %8s %8s   %s\n",
  1371.         "ID", "wtd", "user", "kernel", "event", "state", "name");
  1372.     overflow = FALSE;
  1373.     snapshotCnt = 0;
  1374.     MASTER_LOCK(sched_MutexPtr);
  1375.     LIST_FORALL(schedReadyQueueHdrPtr,itemPtr) {
  1376.         if (snapshotCnt >= SCHED_MAX_DUMP_SIZE) {
  1377.         overflow = TRUE;
  1378.         break;
  1379.         }
  1380.         snapshot[snapshotCnt++] = (Proc_ControlBlock *) itemPtr;
  1381.     }
  1382.     MASTER_UNLOCK(sched_MutexPtr);
  1383.     for (i = 0; i <snapshotCnt; i++) {
  1384.         Proc_DumpPCB(snapshot[i]);
  1385.     }
  1386.     if (overflow) {
  1387.         printf("Ready queue too large to snapshot.\n");
  1388.     }
  1389.     }
  1390. }
  1391.  
  1392.  
  1393. /*
  1394.  * Temporary call-back for printing sched statistics for recovery.
  1395.  */
  1396. Timer_QueueElement      schedStatElement;
  1397. Boolean                 getSchedStats = FALSE;
  1398.  
  1399. /*ARGSUSED*/
  1400. void
  1401. SchedPrintSchedStats(time, clientData)
  1402.     Timer_Ticks time;
  1403.     ClientData  clientData;
  1404. {
  1405.     int                 i;
  1406.  
  1407.     /* print stuff */
  1408.     Sched_PrintStat();
  1409.     for (i = 0; i < mach_NumProcessors; i++) {
  1410.     printf("processor %d:\n", i);
  1411.         printf("idleTicksLow: %d\n",
  1412.         sched_Instrument.processor[i].idleTicksLow);
  1413.         printf("idleTicksOverflow: %d\n",
  1414.         sched_Instrument.processor[i].idleTicksOverflow);
  1415.     }
  1416.     printf("\n");
  1417.  
  1418.     if (getSchedStats) {
  1419.         Timer_ScheduleRoutine(&schedStatElement, TRUE);
  1420.     }
  1421.     return;
  1422. }
  1423.  
  1424.  
  1425.  
  1426. /*
  1427.  *----------------------------------------------------------------------
  1428.  *
  1429.  * Sched_StartSchedStats --
  1430.  *
  1431.  *      Start up the kernel's periodic printing of sched stats.
  1432.  *      Temporary routine for recovery statistics.
  1433.  *
  1434.  * Results:
  1435.  *      None.
  1436.  *
  1437.  * Side effects:
  1438.  *      Call-back routine scheduled.
  1439.  *
  1440.  *----------------------------------------------------------------------
  1441.  */
  1442. void
  1443. Sched_StartSchedStats()
  1444. {
  1445.     schedStatElement.routine = SchedPrintSchedStats;
  1446.     schedStatElement.clientData = 0;
  1447.     schedStatElement.interval = timer_IntOneSecond * 10;
  1448.     getSchedStats = TRUE;
  1449.     Timer_ScheduleRoutine(&schedStatElement, TRUE);
  1450.  
  1451.     return;
  1452. }
  1453.  
  1454.  
  1455. /*
  1456.  *----------------------------------------------------------------------
  1457.  *
  1458.  * Sched_StopSchedStats --
  1459.  *
  1460.  *      Stop the kernel's periodic printing of sched stats.
  1461.  *      Temporary routine for recovery statistics.
  1462.  *
  1463.  * Results:
  1464.  *      None.
  1465.  *
  1466.  * Side effects:
  1467.  *      Call-back routine descheduled.
  1468.  *
  1469.  *----------------------------------------------------------------------
  1470.  */
  1471. void
  1472. Sched_StopSchedStats()
  1473. {
  1474.     getSchedStats = FALSE;
  1475.     (void) Timer_DescheduleRoutine(&schedStatElement);
  1476.  
  1477.     return;
  1478. }
  1479.  
  1480.